
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <map>
#include <algorithm>

#include "../core/tconstraints.h"
#include "../meshdef/mesh.h"
#include "../meshdef/mlsMeshDeformer.h"
#include "../meshdef/mlsSkelMeshDeformer.h"
#include "../meshdef/mlsBlenSkelMeshDeformer.h"
#include "../meshdef/gskeleton.h"

#include <GL/glut.h>
#include "../arcball.h"
#include "../atmatrix.h"
#include "../selection.h"
#include "../glDrawFunctions.h"
#include "../glDrawFunctionsSkel.h"
#include "../typedefs.h"
#include "stroke2d.h"

using namespace std;

int winw = 700, winh = 700; // window size
int xini, yini; // initial mouse position
int buttonpressed; // 
MeshRenderMode rendermode; // mesh render mode
InteractionMode interactionmode; // interaction mode

Point3 grabStart, grabLast;
int id_moving;

AMatrix<GLfloat>     sceneT;
AMatrix<GLfloat>     sceneIniT;
ArcBall              arcball;
Selection            selection;

typedef TConstraints<Point3>                 Constraints;  /// point-based
//~ typedef TConstraints<Segment3>                Constraints;   /// segment-based
//typedef MlsSkelMeshDeformer<Constraints>        MLSskelMeshDeformer;
typedef MlsBlenSkelMeshDeformer<Constraints>    MLSskelMeshDeformer;
MLSskelMeshDeformer                             *deformer=0;

//~ typedef TConstraints<Point3>                 PConstraints;
//~ typedef MlsMeshDeformer<PConstraints>        MLSMeshDeformer;
//~ MLSMeshDeformer                              *deformer=0;

Mesh                                         *mesh=0;
Mesh                                         *meshIni=0;

Constraints constraints;
Constraints constraintsIni;

GSkeleton *skeleton=0, *skeletonOri=0;
int rigskin_mask = RIGGING;
vector<Point3> positionsIni;

Stroke stroke;

/// time info
float      cfps;
time_t     start, end;
unsigned   frame;
/// Info variables
void* font=GLUT_BITMAP_TIMES_ROMAN_24;
void* fontsmall=GLUT_BITMAP_HELVETICA_12;
string nvertices("Vertices: ");
string nfaces("Faces: ");
string fps("FPS: ");
string options("Options: p/w/h/l/f/s/+/-");
string mode("Mode: Viewing");
string nconstraints("constraints: ");

/// Renders info about the model
void renderInfo();
/// Returns the translation vector in world coordinates of two points in screen space
Vector3 screenToWorld(int xi, int yi, int xe, int ye);
/// Returns the world coordinates of a point in screen space
Point3 screenToWorld(int x, int y);
/// Implements the "zoomin" operation. Narrows the view of the scene.
void zoomIn();
/// Implements the "zoomout" operation. Enlarges the view of the scene.
void zoomOut();

//vector<Point3> pstarl;
//vector<Point3> qstarl;
bool show_skeleton=true, show_pstarl=false, show_qstarl=false;
bool show_vhatl=false, show_vhatRotl=false;
bool show_mesh=true, show_mesh_ini=false;

struct compare {
  bool operator()(float a, float b) const {
    return a > b;
  }
};

void printSkeleton_kdist(GSkeleton &skel) {
   
   cout<<"skel.kdist.size(): "<<skel.kdist.size()<<endl;
   for (uint i=0; i<skel.kdist.size(); ++i) {
      cout<<i<<" "<<skel.kdist[i]<<endl;
   }
}

unsigned handle;
vector<unsigned> fixedcp;
void processPaths() {
   
   cout<<"processPaths()"<<endl;
   handle = 9;
   fixedcp.resize(2);
   fixedcp[0] = 3;
   fixedcp[1] = 14;
   
   multimap<float, vector<unsigned>, compare> paths;
   for (unsigned i=0; i<fixedcp.size(); ++i) {
      float l = skeleton->pathLength(fixedcp[i], handle);
      vector<unsigned> path = skeleton->path(fixedcp[i], handle);
      paths.insert(make_pair(l, path));
      copy(path.begin(), path.end(), ostream_iterator<unsigned>(cout, " "));
      cout<<endl;
   }
   
   multimap<float, vector<unsigned> >::iterator it = paths.begin();
   while (it != paths.end()) {
      const pair<float, vector<unsigned> > &item = *it++;
      const vector<unsigned> &path = item.second;
      float D = skeleton->pathLength(path[0], handle);
      for (unsigned i=1; i<path.size(); ++i) {
         if (not skeleton->visited[path[i]]) {
            float d = skeleton->pathLength(path[i], handle);
            skeleton->D[path[i]] = 1.0 - (d/D);
         }
      }
      //cout<<item.first<<" "<<item.second.size()<<endl;
   }
   
//   cout<<"skeleton->D.size(): "<<skeleton->D.size()<<endl;
//   for (uint i=0; i<skeleton->D.size(); ++i) {
//      cout<<i<<" "<<skeleton->D[i]<<endl;
//   }
   
   float K = 300; float M = 400;
   multimap<float, vector<unsigned> >::iterator it1 = paths.begin();
   while (it1 != paths.end()) {
      const pair<float, vector<unsigned> > &item = *it1++;
      const vector<unsigned> &path = item.second;
      int i = 0;
      for (unsigned j=0; j<path.size(); ++j) {
         while (stroke.d[i] <= skeleton->D[path[j]] and i<stroke.size()) i++;
         float gamma = (stroke.d[i]-skeleton->D[path[j]])/(stroke.d[i]-stroke.d[i-1]);
         //~ skeleton->kdist[path[j]] = gamma*stroke.alpha[i-1] + (1.0-gamma)*stroke.alpha[i];
         skeleton->kdist[path[j]] = K*(gamma*stroke.alpha[i-1] + (1.0-gamma)*stroke.alpha[i]) + M;
      }
   }
   
   //~ cout<<"antes"<<endl;
   //~ printSkeleton_kdist(*skeleton);
   //~ cout<<"processPaths() end"<<endl;
   
   //~ vector<float>::const_iterator itmin, itmax;
   //~ itmin = min_element(skeleton->kdist.begin(), skeleton->kdist.end());
   //~ itmax = max_element(skeleton->kdist.begin(), skeleton->kdist.end());
   //~ cout<<"The smallest/largest element is "<<*itmin<<" "<<*itmax<<endl;
   //~ float imin, imax; // initial smallest/largest
   //~ float tmin, tmax; // target smallest/largest
   //~ imin = *itmin; imax = *itmax;
   //~ tmin = 300; tmax = 600;
   //~ float factor = (tmax - tmin)/(imax - imin);
   //~ for (uint i=0; i<skeleton->kdist.size(); ++i) {
      //~ if (skeleton->kdist[i] == 0) continue;
      //~ skeleton->kdist[i] = factor*(skeleton->kdist[i] - imin);
   //~ }
   cout<<"depois"<<endl;
   printSkeleton_kdist(*skeleton);
   cout<<"processPaths() end"<<endl;
   
   
   deformer = new MLSskelMeshDeformer(skeleton, constraints, mesh);
   // first case
   const Point3 &p = constraints.getConstraint(2);
   Vector3 t(0.0,-5.0,10.0);
   constraints.setConstraint(2, p + t);
   
   // second case
   //const Point3 &p = constraints.getConstraint(3);
   //Vector3 t(9.0,-5.0,0.0);
   //constraints.setConstraint(3, p + t);
   
   deformer->pstarl.resize(mesh->nvertices());
   deformer->qstarl.resize(mesh->nvertices());
   deformer->execute(constraints);
}

/// OpenGL display function
void display(void) {
   
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho(10,40,-20,10,-20,60);
   //~ glOrtho(-1.5,1.5,-1.5,1.5,-4,4);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   glViewport(0, 0, (GLsizei) winw, (GLsizei) winh);
   
   glMultMatrixf(&sceneT);

   glTranslatef(12,-2,0);
   glRotatef(90, 0,1,0);
   
   if (show_mesh)       glDrawModel(mesh, rendermode);
   if (show_mesh_ini)   glDrawModel(meshIni, rendermode);
   
//   if (interactionmode == DEFORMATION_MODE) {
//      glPushAttrib(GL_LIGHTING_BIT);
//      glDrawRiggingSkinning(mesh, skeleton, rigskin_mask);
//      glPopAttrib();
//   }
   
   if (show_skeleton)   drawSkeleton(skeleton);
   
   /// draw constraints
   //~ if (flagCtrlPoints) {
      glPushAttrib(GL_COLOR_BUFFER_BIT | GL_LIGHTING_BIT | GL_POINT_BIT);
      glDisable(GL_LIGHTING);
      glEnable(GL_POINT_SMOOTH);
      glEnable(GL_BLEND);
      glDisable(GL_DEPTH_TEST);
      glColor4f(0,0,1,1);
      glPointSize(20);
      glDrawConstraints(constraints);
      glPointSize(1.0);
      glPopAttrib();
      glEnable(GL_DEPTH_TEST);
   //~ }
   
//   stroke.draw();
   
   glPushAttrib(GL_COLOR_BUFFER_BIT | GL_LIGHTING_BIT | GL_POINT_BIT | GL_LINE_BIT);
   glDisable(GL_DEPTH_TEST);
   glDisable(GL_LIGHTING);
   glEnable(GL_POINT_SMOOTH);
   glEnable(GL_BLEND);
   glPointSize(15);
   glBegin(GL_POINTS);
   if (show_pstarl) {
      glColor4f(0,0,0.5,0);
      for (int i=0; i<deformer->pstarl.size(); ++i) 
         glVertex3f(deformer->pstarl[i][0],deformer->pstarl[i][1],deformer->pstarl[i][2]);
   }
   if (show_qstarl) {
      glColor4f(0.5,0,0,0);
      for (int i=0; i<deformer->qstarl.size(); ++i) {
         glVertex3f(deformer->qstarl[i][0],deformer->qstarl[i][1],deformer->qstarl[i][2]);
      }
   }
   glEnd();
   glBegin(GL_LINES);
   if (show_vhatl) {
      glColor4f(0.5,0.5,0,0);
      Vertex_iterator vi = mesh->vertices_begin();
      for (int i=0; i<mesh->nvertices(); ++i) {
         const Point3 &p = vi->point();
         glVertex3f(p[0],p[1],p[2]);
         glVertex3f(deformer->pstarl[i][0],deformer->pstarl[i][1],deformer->pstarl[i][2]);
         ++vi;
      }
   }
   if (show_vhatRotl) {
      glColor4f(0.0,0.5,0.5,0);
      Vertex_iterator vi = mesh->vertices_begin();
      for (int i=0; i<mesh->nvertices(); ++i) {
         const Point3 &p = vi->point();
         glVertex3f(p[0],p[1],p[2]);
         glVertex3f(deformer->qstarl[i][0],deformer->qstarl[i][1],deformer->qstarl[i][2]);
         ++vi;
      }
   }
   glEnd();
   glPointSize(1.0);
   glPopAttrib();
   
   /// FPS info
   frame++;
   std::time(&end);
   double ddifftime = std::difftime(end, start);
   if (ddifftime > 1) {
      cfps = frame/ddifftime;
      start = end;
      frame = 0;
      ostringstream s1;
      s1<<cfps;
      fps = "FPS: " + s1.str();
   }
   
   glPushMatrix();
   glLoadIdentity();
   renderInfo();
   glPopMatrix();
   
   glutSwapBuffers();
}

/// Mouse press function, 
void mouseclick(int button, int state, int x, int y) {
   
   id_moving = -1;
   if (state == GLUT_DOWN) {
      xini=x; yini=winh-y;
      buttonpressed = button;
      //~ if (buttonpressed == GLUT_LEFT_BUTTON and interactionmode == DEFORMATION_MODE) {
      if (buttonpressed == GLUT_LEFT_BUTTON) {
         stroke.clear();
         
         //~ std::list<unsigned int> selected;
         //~ selection.begin(x, winh-y);
         //~ glDrawConstraints(constraints);
         //~ selection.end(selected);
         //~ if (not selected.empty()) id_moving = selected.front();
         
         grabLast = grabStart = screenToWorld(x, winh-y);
      }
      else if (buttonpressed == GLUT_MIDDLE_BUTTON) {
         arcball.click(xini,yini);
      }
      else if (buttonpressed == GLUT_RIGHT_BUTTON) {
         grabLast = grabStart = screenToWorld(x, winh-y);
      }
      sceneIniT = sceneT;
   }
   else if (state == GLUT_UP) {
      if (buttonpressed == GLUT_LEFT_BUTTON and interactionmode == VIEW_MODE) {
         
         if (stroke.size() > 3) {
            stroke.subsampleStroke();
            processPaths();
            //cout<<"stroke.size(): "<<stroke.size()<<endl;
         }
         
         y = winh-y;
         std::list<unsigned int> selected;
         if (abs(xini-x)<4 or abs(yini-y)<4) {
            //~ selection.begin(x, y);
            //~ drawSkeletonJoints(skeleton);
            //~ selection.end(selected);
            //~ // Include the object being altered
            //~ if (not selected.empty()) {
               //~ unsigned id = selected.front();
               //~ Point3 p = skeleton->getJointPos(id);
               //~ constraints.push_back(p, id);
            //~ }
         }
         else {
            //~ selection.begin((int)(xini+0.5*(x-xini)), (int)(yini+0.5*(y-yini)), abs(x-xini), abs(y-yini));
            //~ drawSkeletonJoints(skeleton);
            //~ selection.endAll(selected);
            
            //~ std::list<unsigned int>::iterator it = selected.begin();
            //~ while (it != selected.end()) {
               //~ unsigned id = *it++;
               //~ const Point3 p = skeleton->getJointPos(id);
               //~ constraints.push_back(p, id);
            //~ }
         }
         // label
         //~ ostringstream s2;
         //~ s2<<constraints.size();
         //~ nconstraints = "constraints: " + s2.str();
      }
      
   }
   
   glutPostRedisplay();
}

AMatrix<float> mT; /// an auxiliar matrix
/// Mouse move function
void mousemove(int x, int y) {
   
   if (buttonpressed == GLUT_LEFT_BUTTON) {
      //~ cout<<"aaaaaaaaaaa"<<endl;
      Point2 p(x, winh - y);
      stroke.push_back(p);
   }
   else if (buttonpressed == GLUT_LEFT_BUTTON and 
      interactionmode == DEFORMATION_MODE and id_moving >= 0) {
      
      Point3 grabEnd = screenToWorld(x, winh-y);
      Vector3 t = grabEnd-grabLast;
      
      
      //~ const Point3 &p = constraints.getConstraint(id_moving);
      //~ constraints.setConstraint(id_moving, p + t);
      
      //deformer->execute(constraints);
      //~ deformer->execute(skeleton);
      
      grabLast = grabEnd;
   }
   else if (buttonpressed == GLUT_MIDDLE_BUTTON) { // rotation handler
      arcball.drag(x, winh-y, &mT);
      sceneT = mT*sceneIniT;
   }
   else if (buttonpressed == GLUT_RIGHT_BUTTON) { // translation handler
      Vector3 t = screenToWorld(xini, yini, x, winh-y);
      
      mT.identity();
      mT.translation(t[0], t[1], t[2]);
      //~ sceneT = mT*sceneIniT;
      sceneT = sceneIniT*mT;
   }
   
   glutPostRedisplay();
}

vector<int> handlesids; // ids of control points handles
Number MT[4][4];

/// OpenGL key press function
void keypress(unsigned char key, int x, int y) {
   
   switch (key) {
      //~ case 'p':
      //~ case 'P': rendermode = POINTS; break;
      case 'w':
      case 'W': rendermode = WIREFRAME; break;
      case 'h':
      case 'H': rendermode = HIDDENLINES; break;
      case 'l':
      case 'L': rendermode = FLATLINES; break;
      case 'f':
      case 'F': rendermode = FLAT; break;
      case 's':
      case 'S': rendermode = SMOOTH; break;
      
      case 'e': show_skeleton = not show_skeleton; break;
      case 'p': show_pstarl = not show_pstarl; break;
      case 'q': show_qstarl = not show_qstarl; break;
      case 'v': show_vhatl = not show_vhatl; break;
      case 'b': show_vhatRotl = not show_vhatRotl; break;
      case '1': show_mesh_ini = not show_mesh_ini; break;
      case '2': show_mesh = not show_mesh; break;
      
      case 'r': { sceneT.identity(); sceneIniT.identity(); break; }
      
      case 'd': {
         interactionmode = DEFORMATION_MODE;
         mode = "Mode: Deformation";
         
         //~ constraintsIni = constraints;
         if (deformer!=0) delete deformer;
         //~ deformer = new MLSMeshDeformer(mesh, constraints, true);
         //~ deformer = new MLSskelMeshDeformer(skeletonOri, mesh);
         //deformer = new MLSskelMeshDeformer(skeletonOri, mesh, constraints);
         deformer = new MLSskelMeshDeformer(skeleton, constraints, mesh);

         // first case
         const Point3 &p = constraints.getConstraint(2);
         Vector3 t(10.0,-5.0,0.0);
         constraints.setConstraint(2, p + t);
         
         // second case
         //const Point3 &p = constraints.getConstraint(3);
         //Vector3 t(9.0,-5.0,0.0);
         //constraints.setConstraint(3, p + t);
         
         deformer->pstarl.resize(mesh->nvertices());
         deformer->qstarl.resize(mesh->nvertices());
         deformer->execute(constraints);
         
         break;
      }
      
      case '+': { zoomIn(); break; }
      case '-': { zoomOut(); break; }
      case 27: {
         //processPaths();
         if (skeleton!=0)     delete skeleton;
         if (skeletonOri!=0)  delete skeletonOri;
         if (mesh!=0)         delete mesh;
         if (meshIni!=0)      delete meshIni;
         exit(1);
      }
   }
   glutPostRedisplay();
}

/// auxiliar variables
GLdouble mMtx[16], pMtx[16];  /// modelview/projection matrix
GLint viewport[4];            /// the viewport

/// Returns the world coordinates of a point in screen space
Point3 screenToWorld(int x, int y) {
   
   glGetIntegerv(GL_VIEWPORT, viewport);
   glGetDoublev(GL_PROJECTION_MATRIX, pMtx);
   glGetDoublev(GL_MODELVIEW_MATRIX, mMtx);
   
   GLdouble xw, yw, zw;
   gluUnProject(x, y, 1.0, mMtx, pMtx, viewport, &xw, &yw, &zw);
   return Point3(xw, yw, zw);
}

/// Returns the translation vector in world coordinates of two points in screen space
Vector3 screenToWorld(int xi, int yi, int xe, int ye) {
   
   glGetIntegerv(GL_VIEWPORT, viewport);
   glGetDoublev(GL_PROJECTION_MATRIX, pMtx);
   glGetDoublev(GL_MODELVIEW_MATRIX, mMtx);
   
   GLdouble ws[3], we[3];
   gluUnProject(xi, yi, 0, mMtx, pMtx, viewport, &ws[0], &ws[1], &ws[2]);
   gluUnProject(xe, ye, 0, mMtx, pMtx, viewport, &we[0], &we[1], &we[2]);
   
   return Vector3(we[0]-ws[0], we[1]-ws[1], we[2]-ws[2]);
}

/// OpenGL reshape funcion
void reshape(int w, int h) {
   
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho(-1,1,-1,1,-4,4);
   
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   winw = w; winh = h;
   glViewport (0, 0, (GLsizei) w, (GLsizei) h);
   glutPostRedisplay();
   
   arcball.setBounds(winw, winh);
}

/// OpenGL initializations
void init() {
   
   glEnable(GL_LIGHTING);
   glEnable(GL_LIGHT0);
   glEnable(GL_LIGHT1);
   glEnable(GL_DEPTH_TEST);
   glEnable(GL_NORMALIZE);
   
   // lighting setup
   GLfloat left_light_position[] =  {0.0f, 2.0f, 2.0f, 0.0f};
   GLfloat right_light_position[] = {0.0f, -2.0f, 2.0f, 0.0f};
   GLfloat left_diffuse_light[] =  {1.0f, 1.0f, 1.0f, 1.0f};
   GLfloat right_diffuse_light[] = {1.0f, 1.0f, 1.0f, 1.0f};
   GLfloat color_front[] = {0.85f, 0.55f, 0.0f, 1.0f};
   GLfloat colr_back[] = {1.0f, 0.0f, 0.0f, 1.0f};
   
   glLightfv(GL_LIGHT0, GL_POSITION, left_light_position);
   glLightfv(GL_LIGHT1, GL_POSITION, right_light_position);
   glLightfv(GL_LIGHT0, GL_DIFFUSE, left_diffuse_light);
   glLightfv(GL_LIGHT1, GL_DIFFUSE, right_diffuse_light);
   glMaterialfv(GL_FRONT, GL_DIFFUSE, color_front);
   glMaterialfv(GL_BACK, GL_DIFFUSE, colr_back);
   
   glClearColor(1,1,1,1);
   glColor4f(1,1,0,1);
   rendermode = SMOOTH;
   interactionmode = VIEW_MODE;
   
   arcball.setBounds(winw, winh);
   sceneT.identity();
   
   std::time(&start);
   frame = 0;
}

/// Implements the "zoomin" operation. Narrows the view of the scene.
void zoomIn() {
   
   AMatrix<float> mT;
   mT.identity();
   mT.scaling(1.2);
   sceneT = mT*sceneT;
}

/// Implements the "zoomout" operation. Enlarges the view of the scene.
void zoomOut() {
   
   AMatrix<float> mT;
   mT.identity();
   mT.scaling(1.0/1.2);
   sceneT = mT*sceneT;
}

/// Renders a string
void renderBitmapString(float x, float y, float z, void *font, const char *string) {
   const char *c;
   glRasterPos3f(x, y,z);
   for (c=string; *c != '\0'; c++) {
      glutBitmapCharacter(font, *c);
   }
}

/// Renders info about the model
void renderInfo() {
   
   glDisable(GL_LIGHTING); glDisable(GL_DEPTH_TEST);
   glColor4f(0,0,0,1);
   renderBitmapString(-0.9,0.90,0,(void *)fontsmall, nvertices.c_str());
   renderBitmapString(-0.9,0.85,0,(void *)fontsmall, nfaces.c_str());
   renderBitmapString(-0.9,0.80,0,(void *)fontsmall, fps.c_str());
   renderBitmapString(-0.9,0.75,0,(void *)fontsmall, options.c_str());
   renderBitmapString(-0.9,0.65,0,(void *)fontsmall, nconstraints.c_str());
   glEnable(GL_LIGHTING); glEnable(GL_DEPTH_TEST);
}

void printInfoAndExit() {
   
   cout<<endl;
   cout<<"Usage: meshdef filename.off"<<endl;
   exit(0);
}

void loadSkinningOnJoints(const string &filename) {
   
   assert(mesh != 0);
   mesh->load_skinningOnJoints(filename.c_str());
   mesh->isClustered = true;
}

void loadSkinningOnBones(const string &filename) {
   
   assert(mesh != 0);
   mesh->load_skinningOnBones(filename.c_str());
   
   Number t;
   Vertex_iterator vi = mesh->vertices_begin();
   while (vi != mesh->vertices_end()) {
      vi->ts.clear();
      for (int i=0; i<skeleton->nBones(); ++i) {
         // Compute the closest point
         const Point3 &vp = vi->point();
         Number d = skeleton->getDistance(vp, i, t);
         vi->ts.push_back(t); // the closest point
      }
      ++vi;
   }
}

void open_mesh(string filename) {
   
   string extensao =filename.substr(filename.size()-3,filename.size());
   if (extensao == "off") {
      mesh = Mesh::load_off(filename.c_str());
      meshIni = Mesh::load_off(filename.c_str());
      ostringstream s1, s2;
      s1<<mesh->nvertices();
      s2<<mesh->nfaces();
      nvertices += s1.str();
      nfaces += s2.str();
   }
   
   
   // homer
   //~ Vertex_iterator vi = mesh->vertices_begin();
   //~ constraints.push_back(vi[600].point(), vi[3089].id());
   //~ constraints.push_back(vi[4173].point(), vi[4173].id());
   //~ constraints.push_back(vi[3209].point(), vi[3209].id());
   //~ constraints.push_back(vi[3218].point(), vi[3218].id());
   //~ constraints.push_back(vi[2816].point(), vi[2816].id());
   //~ constraints.push_back(vi[2938].point(), vi[2938].id());
   //~ constraints.push_back(vi[734].point(), vi[734].id());
   //~ constraints.push_back(vi[4068].point(), vi[4068].id());
   //~ constraints.push_back(vi[2974].point(), vi[2974].id());
   //~ constraints.push_back(vi[2973].point(), vi[2973].id());
   //~ constraints.push_back(vi[3961].point(), vi[3961].id());
   
   /*
   // cactus
   Vertex_iterator vi = mesh->vertices_begin();
   constraints.push_back(vi[3089].point(), vi[3089].id());
   constraints.push_back(vi[1852].point(), vi[1852].id());
   constraints.push_back(vi[3094].point(), vi[3094].id());
   constraints.push_back(vi[5032].point(), vi[5032].id());
   constraints.push_back(vi[3864].point(), vi[3864].id());
   constraints.push_back(vi[4949].point(), vi[4949].id());
   constraints.push_back(vi[349].point(), vi[349].id());
   constraints.push_back(vi[3054].point(), vi[3054].id());
   constraints.push_back(vi[571].point(), vi[571].id());
   constraints.push_back(vi[3674].point(), vi[3674].id());
   constraints.push_back(vi[2138].point(), vi[2138].id());
   constraints.push_back(vi[4558].point(), vi[4558].id());
   */
   
   /// ///
   //~ ifstream file("cactus.sel");
   //~ if (not file.is_open()) {
      //~ cout<<"I/O error!!!"<<endl;
      //~ assert(false);
   //~ }
   //~ int idv=0; int t=0;
   //~ while(not file.eof()) {
      //~ file>>t;
      //~ if (t == 0 /*or t == 2*/) {
         //~ Vertex_iterator vi = mesh->vertices_begin();
         //~ advance(vi, idv);
         //~ constraints.push_back(vi->point(), vi->id());
         //~ if (t==2) handlesids.push_back(constraints.size()-1);
      //~ }
      //~ else if (t == 1) {      }
      //~ idv++;
   //~ }
   //~ Vertex_iterator vi = mesh->vertices_begin();
   //~ constraints.push_back(vi[4558].point(), vi[4558].id());
   
   /*
   //~ ifstream fileT("cactus.def");
   //~ if (not fileT.is_open()) {
      //~ cout<<"I/O error!!!"<<endl;
      //~ assert(false);
   //~ }
   //~ fileT>>MT[0][0]>>MT[0][1]>>MT[0][2]>>MT[0][3];
   //~ fileT>>MT[1][0]>>MT[1][1]>>MT[1][2]>>MT[1][3];
   //~ fileT>>MT[2][0]>>MT[2][1]>>MT[2][2]>>MT[2][3];
   //~ fileT>>MT[3][0]>>MT[3][1]>>MT[3][2]>>MT[3][3];
   */
   
   string fname("8.ske");
   string fnamedef("homer19def.ske");
   
   //~ string fname("bar0_2b.ske");
   //~ string fnamedef("bar0_2b_def.ske");
   //~ string fname("bar0_16b.ske");
   //~ string fnamedef("bar0_16b_def.ske");
   //~ string fname("corner0.ske");
   //~ string fnamedef("corner0_def.ske");
   skeleton = GSkeleton::load(fname);
   skeletonOri = GSkeleton::load(fname);
   //~ string fnameBonesWeights("homer_01.sob");
   //~ loadSkinningOnBones(fnameBonesWeights);
   
   cout<<"skeleton->kdist.size(): "<<skeleton->kdist.size()<<endl;
   
   string fnameJointsWeights("8.soj");
   loadSkinningOnJoints(fnameJointsWeights);
   
   //~ string fname("female_01.ske");
   //~ skeleton = GSkeleton::load(fname);
   //~ string fnameweights("female_01.sob");
   //~ loadSkinningOnBones(fnameweights);
   
   /// skeleton constraints

   /// first case
   constraintsIni.push_back(skeletonOri->getJointPos(3), 3);
   constraintsIni.push_back(skeletonOri->getJointPos(14), 14);
   constraintsIni.push_back(skeletonOri->getJointPos(9), 9);

   /// second case
   //constraintsIni.push_back(skeletonOri->getJointPos(3), 3);
   //constraintsIni.push_back(skeletonOri->getJointPos(14), 14);
   //constraintsIni.push_back(skeletonOri->getJointPos(6), 6);
   //constraintsIni.push_back(skeletonOri->getJointPos(17), 17);
   
   constraints = constraintsIni;
   
   generateColors();
   assert(mesh != 0);
}

/// Process the program's parameters
void input(int argc, char **argv) {
   
   int nargs = argc; // 
   if (nargs<2 or nargs>2) printInfoAndExit();
   
   open_mesh(argv[1]);
}

int main(int argc, char **argv) {
   
   // Process the input parameters
   input(argc, argv);
   
   // Glut setup
   glutInit(&argc, argv);
   glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
   glutInitWindowSize(winw, winh);
   glutInitWindowPosition(150, 0);
   glutCreateWindow("MLS mesh deformation prototype");
   
   // OpenGL and data initialization
   init();
   
   // Callbacks registration
   glutDisplayFunc(display);
   glutReshapeFunc(reshape);
   glutMouseFunc(mouseclick);
   glutMotionFunc(mousemove);
   glutKeyboardFunc(keypress);
   glutMainLoop();
   
   return 0;
}

